/*
 * Copyright Aristool AG Switzerland
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

package org.opprua.dss.application;

import java.lang.reflect.Method;

import org.opprua.dss.adapter.OppruaSpringServlet;
import org.opprua.dss.data.Result;
import org.opprua.tidy.beans.BeanHandler;
import org.opprua.tidy.util.XReader;
import org.w3c.dom.Element;

/**
 * TODO: add Java doc
 * 
 * Prepared for first commit: 23.02.2007
 * 
 * @author Lin Li
 */
public class DCall extends XReader {

  private String paramType;
  private String returnType;
  private Method toCall;
  private int paramLength;

  @Override
  public void read(Element e) {
  }

  public void register(DApplication app) {
    app.getProvider(getProvider()).register(this);
  }

  public Result<?> call(Object param) {
    RBase b = OppruaSpringServlet.getLead();
    Object o = b.getBean(getProvider());
    if (o == null) {
      Object s = null;
      try {
        Class<? extends Object> c = RUtil.getScriptable(returnType);
        s = (Object) BeanHandler.fake(c);
      } catch (Throwable e) {}
      return Result.success(s);
    }

    if (toCall == null) {
      Class<?> c = o.getClass();
      Method[] m = c.getMethods();
      for (int i = 0; i < m.length; i++) {
        if (!m[i].getName().equals(getMethod())) continue;
        Class<?>[] pl = m[i].getParameterTypes();
        if (pl.length == 1 && param != null && pl[0].isAssignableFrom(param.getClass())
                    || pl.length == 0 && param == null) {
          toCall = m[i];
          paramLength = pl.length;
          break;
        }
      }
      if (toCall == null) { throw new Error("No method found on bean " + getProvider()
                  + " with name " + getMethod()); }
    }
    try {
      Object[] p = paramLength == 1 ? new Object[] { param } : new Object[] {};
      return (Result<?>) toCall.invoke(o, p);
    } catch (Throwable t) {
      throw new Error("Application exception", t);
    }
  }

  public String getProvider() {
    return getAttribute("provider");
  }

  public String getMethod() {
    return getAttribute("method");
  }

  public String getRelay() {
    return getAttribute("relayTo");
  }

  public String getParamType() {
    return paramType;
  }

  public String getReturnType() {
    return returnType;
  }

  public void setParamType(String callType) {
    this.paramType = callType;
  }

  public void setReturnType(String returnType) {
    this.returnType = returnType;
  }
}
